﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: ObjectSet_1.cs
//
//  Description: In memory cache of persistent objects.
// 
//--------------------------------------------------------------------------

using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Chili.Opf3.Query;
using Chili.Opf3.Storages;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Data;
using Chili.Opf3.Mapping;

namespace Chili.Opf3
{
	/// <summary>
	/// Represents an in-memory cache of persistent objects.
	/// </summary>
	/// <remarks>
	/// <para>
	/// The ObjectSet, which is an in-memory cache of persistent objects retrieved from the storage,
	/// is a major component of the framework. It consists of a collection that contains objects of 
	/// the type specified as generic argument.
	/// </para>
	/// <para>
	/// The class contains also a list of removed items. Use <see cref="Opf3.ObjectSet{T}.Remove">Remove</see> or 
	/// <see cref="Opf3.ObjectSet{T}.RemoveAt">RemoveAt</see> to mark objects for deletion. They are moved to the 
	/// <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> and deleted the next time the ObjectSet is saved. 
	/// Objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> can also be moved back to the
	/// ObjectSet (or removed from the RemovedList) if you won't delete them.
	/// </para>
	/// <para>
	/// The changes on the ObjectSet are saved using the 
	/// <see cref="Opf3.ObjectContext.PersistChanges{T}(T)">PersistChanges</see> method of the 
	/// <see cref="Opf3.ObjectContext">ObjectContext</see>.
	/// </para>
	/// </remarks>
	/// <example>
	/// The following example shows how to use the ObjectSet in an application.
	///		<code lang="CS">
	/// [Persistent("USER")]
	/// public class User
	/// {
	///		private string _name;
	/// 
	///		[Field("NAME")]
	///		public string Name
	///		{
	///			get { return _name; }
	///			set { _name = value; }
	///		}
	/// 
	///		// ... Other properties and fields.
	/// }
	/// 
	/// // ... Other code
	/// 
	/// // Loads an ObjectSet of User from the storage and sorts the result descending.
	/// ObjectSet&lt;User&gt; objectSet = context.GetObjectSet&lt;User&gt;
	///		("UserName like {0} SortBy UserName Desc", "%mit%");
	/// 
	/// if (objectSet.Count > 3)
	/// {
	///		// Change a property of the first element in the
	///		// ObjectSet.
	///		User user = objectSet[0];
	///		user.Name = "new Name";
	/// 
	///		// Mark the second and thirth element for deletion.
	///		objectSet.Remove(objectSet[1]);
	///		// When marking the thirth element we use 1 as index
	///		// since the second object has been already moved
	///		// to the RemovedList.
	///		objectSet.RemoveAt(1);
	/// 
	///		// Restore the first element in the RemovedList.
	///		user = objectSet.RemovedList[0];
	///		objectSet.RemovedList.RemoveAt(0);
	///		objectSet.Add(user);
	/// }
	/// 
	/// // Save the changes.
	/// context.StartTransaction();
	/// context.PersistChanges&lt;User&gt;(objectSet);
	/// context.Commit();
	///		</code>	
	/// </example>
	[Serializable]
	public class ObjectSet<T> : IList<T>, IList, ICustomPersister, IEnumerable, IEnumerable<T>, IListSource, IRaiseItemChangedEvents
	{
		#region SetOperations

		/// <summary>
		/// This class defines some of the common operations on sets.
		/// </summary>
		private sealed class SetOperations
		{
			private IList<IMemberInfo> _identifier = new List<IMemberInfo>();

			/// <summary>
			/// Creates a new instance of the <see cref="SetOperations">SetOperations Class</see>.
			/// </summary>
			public SetOperations()
			{
				// Get the identifier PropertyInfo objects and store them.
				TypeMapping typeMapping = TypeMapping.GetTypeMapping(typeof(T));
				foreach (IMemberInfo info in typeMapping.Members)
				{
					if (info.FieldAttribute.Identifier)
						_identifier.Add(info);
				}
			}

			/// <summary>
			/// Compares the identifier of two different objects.
			/// </summary>
			private int Compare(T x, T y)
			{
				foreach (IMemberInfo info in _identifier)
				{
					object v1 = info.GetValue(x);
					object v2 = info.GetValue(y);

					IComparable comparable = v1 as IComparable;
					if (comparable != null)
					{
						int val = comparable.CompareTo(v2);
						if (val == 0)
							continue;
						return val;
					}
				}
				return 0;
			}

			/// <summary>
			/// All duplicates in the <see cref="ObjectSet{T}">ObjectSet</see> are removed.
			/// </summary>
			/// <param name="objectSet">The <see cref="ObjectSet{T}">ObjectSet</see> where the duplicates are removed.</param>
			public void Distinct(ObjectSet<T> objectSet)
			{
				objectSet.Sort(new Comparison<T>(Compare));

				for (int i = objectSet.Count - 1; i > 0; i--)
				{
					T v1 = objectSet[i];
					T v2 = objectSet[i - 1];

					if (Compare(v1, v2) == 0)
					{
						objectSet.RemoveAt(i);
//						objectSet.RemovedList.RemoveAt(objectSet.RemovedList.Count - 1);
					}
				}
			}

			/// <summary>
			/// Unites two <see cref="ObjectSet{T}">ObjectSets</see>. Duplicates are removed.
			/// </summary>
			public void Union(ObjectSet<T> x, ObjectSet<T> y)
			{
				x.AddRange(y);
				Distinct(x);
			}

			/// <summary>
			/// Creates the intersecting set of two <see cref="ObjectSet{T}">ObjectSet</see> instances.
			/// </summary>
			public void Intersect(ObjectSet<T> x, ObjectSet<T> y)
			{
				Distinct(x);
				IDictionary<string, T> table = BuildObjectTable(y);

				for (int i = x.Count - 1; i >= 0; i--)
				{
					string id = GetIdentifiers(x[i]);

					if (!table.ContainsKey(id))
					{
						x.RemoveAt(i);
//						x.RemovedList.RemoveAt(x.RemovedList.Count - 1);
					}
				}
			}

			/// <summary>
			/// Builds an table containing the identifiers of the <see cref="ObjectSet{T}">ObjectSet</see>.
			/// </summary>
			private IDictionary<string, T> BuildObjectTable(ObjectSet<T> objectSet)
			{
				IDictionary<string, T> objectTable = new Dictionary<string, T>();
				foreach (T obj in objectSet)
				{
					string id = GetIdentifiers(obj);

					if (!objectTable.ContainsKey(id))
						objectTable.Add(id, obj);
				}

				return objectTable;
			}

			/// <summary>
			/// Gets the identifiers of an object.
			/// </summary>
			private string GetIdentifiers(T obj)
			{
				StringBuilder identifier = new StringBuilder();

				foreach (IMemberInfo info in _identifier)
				{
					object value = info.GetValue(obj);
					if (value != null)
					{
						string stringValue = value.ToString();
						identifier.AppendFormat("{0}{1}", stringValue, stringValue.Length);
					}
				}
				return identifier.ToString();
			}

			/// <summary>
			/// Creates the difference in quantities of two <see cref="ObjectSet{T}">ObjectSet</see> instances.
			/// </summary>
			public void Except(ObjectSet<T> x, ObjectSet<T> y)
			{
				Distinct(x);
				IDictionary<string, T> table = BuildObjectTable(y);

				for (int i = x.Count - 1; i >= 0; i--)
				{
					string id = GetIdentifiers(x[i]);

					if (table.ContainsKey(id))
					{
						x.RemoveAt(i);
//						x.RemovedList.RemoveAt(x.RemovedList.Count - 1);
					}
				}
			}
 		}

		#endregion

		private List<T> _list;
		private List<T> _removedList = new List<T>();
		private List<T> _transactionRemovedList;
		[NonSerialized]
		private ObjectView<T> _defaultView;
		[NonSerialized]
		private ObjectViewManager<T> _objectViewManager;
		private bool _raiseItemChangedEvents;
		private PropertyChangedEventHandler _propertyChangedEventHandler;

		#region Constructors

		/// <summary>
		/// Creates a new instance of the <see cref="Opf3.ObjectSet{T}">ObjectSet Class</see>.
		/// </summary>
		public ObjectSet()
		{
			_list = new List<T>();
			Initialize();
		}

		/// <summary>
		/// Creates a new instance of the <see cref="Opf3.ObjectSet{T}">ObjectSet Class</see>.
		/// </summary>
		/// <param name="capacity">The number of items that can be stored by the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		public ObjectSet(int capacity)
		{
			_list = new List<T>(capacity);
			Initialize();
		}

		/// <summary>
		/// Creates a new instance of the <see cref="Opf3.ObjectSet{T}">ObjectSet Class</see>.
		/// </summary>
		/// <param name="list">List that contains the data to populate the 
		///	<see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		public ObjectSet(IList<T> list)
		{
			_list = new List<T>(list);
			Initialize();
		}

		/// <summary>
		/// Creates a new instance of the <see cref="Opf3.ObjectSet{T}">ObjectSet Class</see>.
		/// </summary>
		/// <param name="objectReader"><see cref="Opf3.ObjectReader{T}">ObjectReader</see> that is used 
		/// to populate the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		public ObjectSet(ObjectReader<T> objectReader) : this()
		{
			// Register the event of the ObjectReader.
			objectReader.FetchingStorageRecord += new PersistentTypeSelector(objectReader_FetchingStorageRecord);

			foreach (T obj in objectReader)
			{
				this.Add(obj);
			}

			Initialize();
		}

		/// <summary>
		/// Creates a new instance of the <see cref="Opf3.ObjectSet{T}">ObjectSet Class</see>.
		/// </summary>
		/// <param name="objectReader"><see cref="Opf3.ObjectReader{T}">ObjectReader</see> that is used 
		/// to populate the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <param name="index">Index where to start with populating.</param>
		/// <param name="amount">Amount of objects that are populated.</param>
		/// <exception cref="System.IndexOutOfRangeException">Index is lower then zero.</exception>
		/// <exception cref="ArgumentException">Amount if lower then zero.</exception>
		public ObjectSet(ObjectReader<T> objectReader, int index, int amount) : this()
		{
			// Don't allow indexes lower then 0.
			if (index < 0)
				throw new IndexOutOfRangeException("Index is lower then zero.");
			if (amount < 0)
				throw new ArgumentException("Amount is lower then zero.", "amount");

			// Register the event of the ObjectReader.
			objectReader.FetchingStorageRecord += new PersistentTypeSelector(objectReader_FetchingStorageRecord);

			// Advance the reader to the index given.
			if (!objectReader.Read(index + 1))
			{
				Tracing.Current.TraceWarning("No object loaded. Loaded amount of objects lower then the index.");
				return;
			}

			// Populate until we reach the amount.
			for (int i = 0; i < amount; i++)
			{
				this.Add(objectReader.Current);
				// Return if no data is left.
				if (!objectReader.Read())
					return;
			}
			
			Initialize();
		}

		/// <summary>
		/// Initializes the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> members appropriately.
		/// </summary>
		private void Initialize()
		{
			// Check if we raise and event if the property of an item changed.
			_raiseItemChangedEvents = typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T));

			// If so register all items in the list.
			if (_raiseItemChangedEvents)
			{
				foreach(T item in this)
				{
					HookPropertyChanged((INotifyPropertyChanged)item);
				}
			}
		}

		#endregion

		#region ItemPropertyChanged Methods

		/// <summary>
		/// Hooks the property changed event.
		/// </summary>
		/// <param name="item">The item that's property changed event is hooked.</param>
		private void HookPropertyChanged(INotifyPropertyChanged item)
		{
			if (item == null)
				return;

			if (_propertyChangedEventHandler == null)
				_propertyChangedEventHandler = new PropertyChangedEventHandler(this.ItemPropertyChanged);

			item.PropertyChanged += this._propertyChangedEventHandler;
		}

		/// <summary>
		/// Unhooks the property changed event.
		/// </summary>
		/// <param name="item">The item that's property changed event is unhooked.</param>
		private void UnHookPropertyChanged(INotifyPropertyChanged item)
		{
			if (item == null || _propertyChangedEventHandler == null)
				return;

			item.PropertyChanged -= this._propertyChangedEventHandler;
		}

		/// <summary>
		/// Invoked each time a property of the child changed.
		/// </summary>
		/// <param name="sender">The item that is sending the event.</param>
		/// <param name="e">The EventArgs class that contains additional information for the event.</param>
		private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			int index = _list.IndexOf((T)sender);
			if (index == -1)
				return;

			// Fire the list changed event.
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));  
		}

		#endregion

		#region Events

		/// <summary>
		/// Event is fired before the data from the storage is used to populate an object.
		/// </summary>
		/// <remarks>
		/// This event is fired before the data from the storage is used to populate an object. It allows the user
		/// to specify another type for the row (item) that is populated with the data. It allows also to have 
		/// certain items ignored. This event can be used in certain scenarios of inheritance.
		/// </remarks>
		/// <example>
		/// The following example introduces to the use of the FecthingStorageRecord event. 
		/// <code lang="CS">
		/// ObjectSet&lt;User&gt; objectSet = new ObjectSet&lt;User&gt;();
		/// // Register the event.
		/// objectSet.FetchingStorageRecord += new PersistentTypeSelector(MySelection);
		/// // Fill the ObjectSet with the data from the storage.
		/// objectSet = (ObjectSet&lt;User&gt;)context.GetObjectList&lt;User&gt;(objectSet);
		/// 
		/// // Loop trough all items.
		/// foreach (User user in objectSet)
		/// {
		///     if (user.GetType() == typeof(User1))
		///         Console.WriteLine("User1 found.");
		///     else
		///         Console.WriteLine("User found.");
		/// }
		/// 
		/// // ...
		/// 
		/// private void MySelection(object sender, FetchingStorageRecordEventArgs e)
		/// {
		///     if (e.DataRecord["Type"] == "1")
		///     {
		///         // Switch the type of the persistent that is created to User1.
		///	        e.Type = typeof(User1);
		///     }
		///     else if ((bool)e.DataRecord["Ignore"])
		///     {
		///         // Tell the framework to ignore this item.
		///         e.Ignore = true;
		///     }
		///     // Have a persistent of type User created.
		///     e.Type = typeof(User);
		/// }
		/// </code>
		/// </example>
		public event PersistentTypeSelector FetchingStorageRecord;

		/// <summary>
		/// Invoked each time the event happens on the ObjectReader.
		/// </summary>
		private void objectReader_FetchingStorageRecord(object sender, FetchingStorageRecordEventArgs e)
		{
			// Fire the event of the ObjectSet.
			if (FetchingStorageRecord != null)
				FetchingStorageRecord(this, e);
		}

		/// <summary>
		/// Occurs when the <see cref="ObjectSet{T}">ObjectSet</see> changes.
		/// </summary>
		/// <remarks>
		/// <para>
		/// This event is fired each time the <see cref="ObjectSet{T}">ObjectSet</see> changes to notify 
		/// that the object has been changed. It allows to act after the object has been changed.
		/// </para>
		/// <para>
		/// This event is also used by the <see cref="ObjectView{T}">ObjectView</see> to get informed, when the 
		/// <see cref="ObjectSet{T}">ObjectSet</see> changes.
		/// </para>
		/// </remarks>
		public event ListChangedEventHandler ListChanged;

		/// <summary>
		/// Invokes the ListChanged event.
		/// </summary>
		/// <param name="e"></param>
		private void OnListChanged(ListChangedEventArgs e)
		{
			// Notify all views that the list has been changed.
			this.ObjectViewManager.NotifyListChanged(e);

			if (ListChanged != null)
				ListChanged(this, e);
		}

		#endregion

		#region IList<T> Members

		/// <summary>
		/// Gets or sets the object at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the object to get or set.</param>
		/// <returns>The object at the specified index.</returns>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less then zero or index is equal or greater
		/// then <see cref="ObjectSet{T}.Count">Count</see>.</exception>
		public T this[int index]
		{
			get 
			{
				return _list[index];
			}
			set 
			{
				// Un hook the event that is fired when a property of the item changes.
				if (_raiseItemChangedEvents)
					UnHookPropertyChanged(_list[index] as INotifyPropertyChanged);

				_list[index] = value;
				
				// Hook the event that is fired when a property of the item changes.
				if (_raiseItemChangedEvents)
					HookPropertyChanged(_list[index] as INotifyPropertyChanged);

				// Invoked the event to notify the user that the list has been moved. The ObjectView<T> subscribes this event
				// and needs ItemMoved here!
				OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, index));
			}
		}

		/// <summary>
		/// Determines the index of a specific item in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="item">The object to locate in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <returns>The index of item if found in the list; otherwise, -1.</returns>
		public int IndexOf(T item)
		{
			return _list.IndexOf(item);
		}

		/// <summary>
		/// Inserts an item to the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> at the specified index. 
		/// </summary>
		/// <param name="index">The zero-based index at which item should be inserted.</param>
		/// <param name="item">The object to insert into the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or index is greater then
		/// <see cref="ObjectSet{T}.Count">Count</see>.</exception>
		public void Insert(int index, T item)
		{
			_list.Insert(index, item);
			
			// Hook the event that is fired when a property of the item changes.
			if (_raiseItemChangedEvents)
				HookPropertyChanged(item as INotifyPropertyChanged);

			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
		}

		/// <summary>
		/// Moves the object at the specified index from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> to
		/// the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>. 
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="index">The zero-based index of the item to move.</param>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is lesso than zero or index is equal to
		/// or greater than <see cref="ObjectSet{T}.Count">Count</see>.</exception>
		public void RemoveAt(int index)
		{
			lock (this)
			{
				T obj = this[index];
				_list.RemoveAt(index);
				this.RemovedList.Add(obj);

				// Un hook the event that is fired when a property of the item changes.
				if (_raiseItemChangedEvents)
					UnHookPropertyChanged(obj as INotifyPropertyChanged);
				
				// Invoked the event to notify the user that the list has been changed.
				OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
			}
		}

		#endregion

		#region ICollection<T> Members

		/// <summary>
		/// Gets the number of elements contained in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// </summary>
		/// <value>The number of elements contained in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</value>
		[XmlIgnore]
		public int Count
		{
			get { return _list.Count; }
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> is read-only.
		/// </summary>
		/// <value>True, if the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> is read-only; otherwise, false.</value>
		[XmlIgnore]
		public bool IsReadOnly
		{
			get { return false; }
		}

		/// <summary>
		/// Adds an item to the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="item">The object to add to the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		public void Add(T item)
		{
			_list.Add(item);

			// Hook the event that is fired when a property of the item changes.
			if (_raiseItemChangedEvents)
				HookPropertyChanged(item as INotifyPropertyChanged);

			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, _list.Count - 1));
		}

		/// <summary>
		/// Removes all items from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		public void Clear()
		{
			// Un hook the event that is fired when a property of the item changes.
			if (_raiseItemChangedEvents)
			{
				foreach(T item in _removedList)
					UnHookPropertyChanged(item as INotifyPropertyChanged);
				foreach(T item in _list)
					UnHookPropertyChanged(item as INotifyPropertyChanged);
			}

			_removedList.Clear();
			_list.Clear();

			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Determines whether the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> contains a specific object.
		/// </summary>
		/// <param name="item">The object to located in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <returns>True, if item is found in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>; otherwise, false.</returns>
		public bool Contains(T item)
		{
			return _list.Contains(item);
		}

		/// <summary>
		/// Copies the elements of the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> to an Array, starting at 
		/// a particular Array index.
		/// </summary>
		/// <param name="array">The one-dimensional Array that is the destination of the elements copied 
		/// from <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
		/// <exception cref="System.ArgumentNullException">Array is null.</exception>
		/// <exception cref="System.ArgumentException">Array is multidimensional, array does not have zero-based
		/// indexing, the number of elements in the source is greater than the number of elements that the 
		/// destination array can contain or type T cannot be cast automatically to the type of the destination
		/// array.</exception>
		public void CopyTo(T[] array, int arrayIndex)
		{
			_list.CopyTo(array, arrayIndex);
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>
		/// to the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="item">The object to remove from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <returns>True, if item was successfully removed from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>; otherwise, false. 
		/// This method also returns false if item was not found in the original 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</returns>
		public bool Remove(T item)
		{
			int index = -1;
			lock (this)
			{
				// Get the index of the item.
				index = _list.IndexOf(item);
			}

			if (index == -1)
			{
				Tracing.Current.TraceWarning("Could not remove object. Item not found.");
				return false;
			}

			// Remove the item.
			this.RemoveAt(index);
			return true;
		}

		#endregion

		#region IEnumerable<T> Members

		/// <summary>
		/// Returns an enumerator that iterates through the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <returns>An enumerator that can be used to iterate through the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</returns>
		public IEnumerator<T> GetEnumerator()
		{
			return _list.GetEnumerator();
		}

		#endregion

		#region IEnumerable Members

		/// <summary>
		/// Returns an enumerator that iterates through this <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return (IEnumerator)GetEnumerator();
		}

		#endregion

		#region List<T> Members

		/// <summary>
		/// Gets or sets the number of elements that the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> can contain. 
		/// </summary>
		/// <value>The number of elements that the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> can contain.</value>
		public int Capacity
		{
			get { return _list.Capacity; }
			set { _list.Capacity = value; }
		}

		/// <summary>
		/// Adds the elements of the specified collection to the end of the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="collection">The collection whose elements should be added to the end of the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. The collection itself cannot be null, but it can 
		/// contain elements that are null, if type T is a reference type.</param>
		/// <exception cref="System.ArgumentNullException">Collection is null.</exception>
		public void AddRange(IEnumerable<T> collection)
		{
			foreach (T obj in collection)
			{
				if (obj == null)
					continue;
				this.Add(obj);
			}
		}

		/// <summary>
		/// Adds the elements of the specified collection to the end of the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="collection">The collection whose elements should be added to the end of the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. The collection itself cannot be null, but it can 
		/// contain elements that are null, if type T is a reference type.</param>
		/// <param name="match">The Predicate delegate that defines the conditions to check against the elements.</param>
		/// <exception cref="System.ArgumentNullException">Collection is null.</exception>
		public void AddRange(IEnumerable<T> collection, Predicate<T> match)
		{
			foreach (T obj in collection)
			{
				if (obj == null)
					continue;

				if (match(obj))
					this.Add(obj);
			}
		}

		/// <summary>
		/// Searches the entire sorted <see cref="Opf3.ObjectSet{T}">ObjectSet</see> for an element using the 
		/// default comparer and returns the zero-based index of the element.
		/// </summary>
		/// <param name="item">The object to locate. The value can be null for reference types.</param>
		/// <returns>The zero-based index of item in the sorted <see cref="Opf3.ObjectSet{T}">ObjectSet</see>, if 
		/// item is found; otherwise, a negative number, which is the bitwise complement of the index 
		/// of the next element that is larger than item or, if there is no larger element, the 
		/// bitwise complement of <see cref="ObjectSet{T}.Count">Count</see>.</returns>
		/// <exception cref="System.InvalidOperationException">The default comparer cannot find an 
		/// IComparable implementation of the Type T.</exception>
		public int BinarySearch(T item)
		{
			return _list.BinarySearch(item);
		}

		/// <summary>
		/// Searches the entire sorted <see cref="Opf3.ObjectSet{T}">ObjectSet</see> for an element 
		/// using the specified comparer and returns the zero-based index of the element. 
		/// </summary>
		/// <param name="item">The object to locate. The value can be null for reference types.</param>
		/// <param name="comparer">The comparer implementation to use when comparing elements or null to use
		/// the default comparer.</param>
		/// <returns>The zero-based index of item in the sorted <see cref="Opf3.ObjectSet{T}">ObjectSet</see>, if 
		/// item is found; otherwise, a negative number, which is the bitwise complement of the index of the 
		/// next element that is larger than item or, if there is no larger element, the bitwise 
		/// complement of <see cref="ObjectSet{T}.Count">Count</see>.</returns>
		/// <exception cref="System.InvalidOperationException">The default comparer cannot find an 
		/// IComparable implementation of the Type T.</exception>
		public int BinarySearch(T item, IComparer<T> comparer)
		{
			return _list.BinarySearch(item, comparer);
		}

		/// <summary>
		/// Searches a range of elements in the sorted <see cref="Opf3.ObjectSet{T}">ObjectSet</see> for an element 
		/// using the specified comparer and returns the zero-based index of the element.
		/// </summary>
		/// <param name="index">The zero-based starting index of the range to search.</param>
		/// <param name="count">The length of the range to search.</param>
		/// <param name="item">The object to locate. The value can be null for reference types.</param>
		/// <param name="comparer">The cComparer implementation to use when comparing elements or null to use
		///  the default comparer.</param>
		/// <returns>The zero-based index of item in the sorted <see cref="Opf3.ObjectSet{T}">ObjectSet</see>, if 
		/// item is found; otherwise, a negative number, which is the bitwise complement of the index of the 
		/// next element that is larger than item or, if there is no larger element, the bitwise 
		/// complement of <see cref="ObjectSet{T}.Count">Count</see>.</returns>
		/// <exception cref="System.InvalidOperationException">The default comparer cannot find an 
		/// IComparable implementation of the Type T.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or 
		/// <see cref="ObjectSet{T}.Count">Count</see> is less then zero.</exception>
		/// <exception cref="System.ArgumentException">Index and count do not denote a valid range in the 
		/// <see cref="ObjectSet{T}">ObjectSet</see>.</exception>
		public int BinarySearch(int index, int count, T item, IComparer<T> comparer)
		{
			return _list.BinarySearch(index, count, item, comparer);
		}

		/// <summary>
		/// Converts the current <see cref="Opf3.ObjectSet{T}">ObjectSet</see> to another type.
		/// </summary>
		/// <param name="converter">A converter delegate that converts each element from one type to another type.</param>
		/// <returns>An <see cref="Opf3.ObjectSet{T}">ObjectSet</see> of the target type containing the 
		/// converted elements from the current <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. </returns>
		/// <exception cref="System.ArgumentNullException">Converter is null.</exception>
		public ObjectSet<U> ConvertAll<U>(Converter<T, U> converter)
		{
			return new ObjectSet<U>(_list.ConvertAll<U>(converter));
		}

		/// <summary>
		/// Determines whether the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> contains elements that 
		/// match the conditions defined by the specified predicate. 
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the elements to search for.</param>
		/// <returns>true, if the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> contains one or more 
		/// elements that match the conditions defined by the specified predicate; otherwise, false.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public bool Exists(Predicate<T> match)
		{
			return _list.Exists(match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the first occurrence within the entire <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The first element that matches the conditions defined by the specified predicate, if found; 
		/// otherwise, the default value for type T. </returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public T Find(Predicate<T> match)
		{
			return _list.Find(match);
		}

		/// <summary>
		/// Retrieves the all the elements that match the conditions defined by the specified predicate.
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the elements to search for.</param>
		/// <returns>An <see cref="Opf3.ObjectSet{T}">ObjectSet</see> containing all the elements that match the 
		/// conditions defined by the specified predicate, if found; otherwise, an empty 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public ObjectSet<T> FindAll(Predicate<T> match)
		{
			return new ObjectSet<T>(_list.FindAll(match));
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, and 
		/// returns the zero-based index of the first occurrence within the entire
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The zero-based index of the first occurrence of an element that 
		/// matches the conditions defined by match, if found; otherwise, -1</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public int FindIndex(Predicate<T> match)
		{
			return _list.FindIndex(match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the first occurrence within the range of elements 
		/// in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> that extends from the specified index 
		/// to the last element. 
		/// </summary>
		/// <param name="startIndex">The zero-based starting index of the search.</param>
		/// <param name="match">The Predicate delegate that defines the conditions of the 
		/// element to search for.</param>
		/// <returns>The zero-based index of the first occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">StartIndex is outside the range of valid indexes
		/// for the <see cref="ObjectSet{T}">ObjectSet</see>.</exception>
		public int FindIndex(int startIndex, Predicate<T> match)
		{
			return _list.FindIndex(startIndex, match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the first occurrence within the range of elements 
		/// in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> that starts at the specified index and contains 
		/// the specified number of elements. 
		/// </summary>
		/// <param name="startIndex">The zero-based starting index of the search.</param>
		/// <param name="count">The number of elements in the section to search.</param>
		/// <param name="match">The Predicate delegate that defines the conditions of the 
		/// element to search for.</param>
		/// <returns>The zero-based index of the first occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">StartIndex is outside the range of valid indexes
		/// for the <see cref="ObjectSet{T}">ObjectSet</see> or Count is less then zero.</exception>
		public int FindIndex(int startIndex, int count, Predicate<T> match)
		{
			return _list.FindIndex(startIndex, count, match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the last occurrence within the entire <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The last element that matches the conditions defined by the specified predicate, if found; 
		/// otherwise, the default value for type T.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public T FindLast(Predicate<T> match)
		{
			return _list.FindLast(match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the last occurrence within the entire 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The zero-based index of the last occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public int FindLastIndex(Predicate<T> match)
		{
			return _list.FindLastIndex(match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the last occurrence within the range of elements in 
		/// the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> that extends from the first element 
		/// to the specified index.
		/// </summary>
		/// <param name="startIndex">The zero-based starting index of the backward search.</param>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The zero-based index of the last occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">StartIndex is outside the range of valid
		/// indexes for the <see cref="ObjectSet{T}">ObjectSet</see>.</exception>
		public int FindLastIndex(int startIndex, Predicate<T> match)
		{
			return _list.FindLastIndex(startIndex, match);
		}

		/// <summary>
		/// Searches for an element that matches the conditions defined by the specified predicate, 
		/// and returns the zero-based index of the last occurrence within the range of elements 
		/// in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> that contains the specified number of elements 
		/// and ends at the specified index. 
		/// </summary>
		/// <param name="startIndex">The zero-based starting index of the backward search.</param>
		/// <param name="count">The number of elements in the section to search.</param>
		/// <param name="match">The Predicate delegate that defines the conditions of the element to search for.</param>
		/// <returns>The zero-based index of the last occurrence of an element that matches the conditions 
		/// defined by match, if found; otherwise, -1. </returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">StartIndex is outside the range of valid indexes
		/// for the <see cref="ObjectSet{T}">ObjectSet</see> or Count is less then zero.</exception>
		public int FindLastIndex(int startIndex, int count, Predicate<T> match)
		{
			return _list.FindLastIndex(startIndex, count, match);
		}

		/// <summary>
		/// Performs the specified action on each element of the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// </summary>
		/// <param name="action">The Action delegate to perform on each element of the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <exception cref="System.ArgumentNullException">Action is null.</exception>
		public void ForEach(Action<T> action)
		{
			_list.ForEach(action);
		}

		/// <summary>
		/// Creates a shallow copy of a range of elements in the source <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="index">The zero-based <see cref="Opf3.ObjectSet{T}">ObjectSet</see> index 
		/// at which the range starts.</param>
		/// <param name="count">The number of elements in the range.</param>
		/// <returns>A shallow copy of a range of elements in the source 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</returns>
		/// <exception cref="System.ArgumentException">Index and count do not denote a valid range of elements
		/// in the <see cref="ObjectSet{T}">ObjectSet</see>.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or count is less than
		/// zero.</exception>
		public ObjectSet<T> GetRange(int index, int count)
		{
			return new ObjectSet<T>(_list.GetRange(index, count));
		}

		/// <summary>
		/// Inserts the elements of a collection into the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> 
		/// at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index at which the new elements should be inserted.</param>
		/// <param name="collection">The collection whose elements should be inserted into 
		/// the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. The collection itself cannot be null, but 
		/// it can contain elements that are null, if type T is a reference type.</param>
		/// <exception cref="System.ArgumentNullException">Collection is null.</exception>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or index is greater
		/// then <see cref="ObjectSet{T}">ObjectSet</see>.</exception>
		public void InsertRange(int index, IEnumerable<T> collection)
		{
			_list.InsertRange(index, collection);

			// Hook the event that is fired when a property of the item changes.
			if (_raiseItemChangedEvents)
			{
				foreach (T item in collection)
					HookPropertyChanged(item as INotifyPropertyChanged);
			}

			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Searches for the specified object and returns the zero-based index of the last 
		/// occurrence within the entire <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// </summary>
		/// <param name="item">The object to locate in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. The 
		/// value can be null for reference types.</param>
		/// <returns>The zero-based index of the last occurrence of item within the entire the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>, if found; otherwise, -1.</returns>
		public int LastIndexOf(T item)
		{
			return _list.LastIndexOf(item);
		}

		/// <summary>
		/// Searches for the specified object and returns the zero-based index of the last occurrence 
		/// within the range of elements in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> that extends 
		/// from the first element to the specified index.
		/// </summary>
		/// <param name="item">The object to locate in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. The 
		/// value can be null for reference types.</param>
		/// <param name="index">The zero-based starting index of the backward search.</param>
		/// <returns>The zero-based index of the last occurrence of item within the range of elements in 
		/// the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> that extends from the first element to index, 
		/// if found; otherwise, -1.</returns>
		public int LastIndexOf(T item, int index)
		{
			return _list.LastIndexOf(item, index);
		}

		/// <summary>
		/// Searches for the specified object and returns the zero-based index of the last occurrence 
		/// within the range of elements in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> that contains 
		/// the specified number of elements and ends at the specified index. 
		/// </summary>
		/// <param name="item">The object to locate in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// The value can be null for reference types.</param>
		/// <param name="index">The zero-based starting index of the backward search.</param>
		/// <param name="count">The number of elements in the section to search.</param>
		/// <returns>The zero-based index of the last occurrence of item within the range of elements 
		/// in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> that contains count number of elements and 
		/// ends at index, if found; otherwise, -1. </returns>
		public int LastIndexOf(T item, int index, int count)
		{
			return _list.LastIndexOf(item, index, count);
		}

		/// <summary>
		/// Removes all the elements in the list to the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		public void RemoveAll()
		{
			// Un hook the event that is fired when a property of the item changes.
			if (_raiseItemChangedEvents)
			{
				foreach(T item in _list)
					UnHookPropertyChanged(item as INotifyPropertyChanged);
			}

			_removedList.AddRange(_list);
			_list.Clear();
			
			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Removes all the elements that match the conditions defined by the specified predicate.
		/// They are moved to the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions of the elements to remove.</param>
		/// <returns>The number of elements removed from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public int RemoveAll(Predicate<T> match)
		{
			if (match == null)
				throw new ArgumentNullException("match");

			int amount = 0;
			for(int i = _list.Count -1; i >= 0; i--)
			{
				T obj = _list[i];

				if (match(obj))
				{
					this.RemoveAt(i);
					amount++;
				}
			}
			return amount;
		}

		/// <summary>
		/// Removes a range of elements from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// They are moved to the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="index">The zero-based starting index of the range of elements to remove.</param>
		/// <param name="count">The number of elements to remove.</param>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less then zero or count is less
		/// then zero.</exception>
		/// <exception cref="System.ArgumentException">Index and count do not denote a valid range of elements
		/// in the <see cref="ObjectSet{T}">ObjectSet</see>.</exception>
		public void RemoveRange(int index, int count)
		{
			// Check if the given values match the ObjectSet's size.
			if (index < 0 || count < 0)
				throw new ArgumentOutOfRangeException("Index is less then zero or Count is less than zero.");
			if (index > this.Count || index + count > this.Count)
				throw new ArgumentException("Index and count do not denote a valid range of elements.");

			// Remove all items until reached count.
			for (int i = 0; i < count; i++)
				this.RemoveAt(index + i);
		}

		/// <summary>
		/// Reverses the order of the elements in the entire <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// </summary>
		public void Reverse()
		{
			_list.Reverse();
			
			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Reverses the order of the elements in the specified range.
		/// </summary>
		/// <param name="index">The zero-based starting index of the range to reverse.</param>
		/// <param name="count">The number of elements in the range to reverse.</param>
		public void Reverse(int index, int count)
		{
			_list.Reverse(index, count);
			
			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Sorts the elements in the entire <see cref="Opf3.ObjectSet{T}">ObjectSet</see> using the default comparer.
		/// </summary>
		/// <exception cref="System.InvalidOperationException">The default comparer cannot find a IComparable
		/// implementation for type T.</exception>
		public void Sort()
		{
			_list.Sort();

			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Sorts the elements in the entire <see cref="Opf3.ObjectSet{T}">ObjectSet</see> using the 
		/// specified Comparison.
		/// </summary>
		/// <param name="comparison">The Comparison to use when comparing elements.</param>
		/// <exception cref="System.ArgumentNullException">Comparison is null.</exception>
		public void Sort(Comparison<T> comparison)
		{
			_list.Sort(comparison);

			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Sorts the elements in the entire <see cref="Opf3.ObjectSet{T}">ObjectSet</see> using 
		/// the specified comparer. 
		/// </summary>
		/// <param name="comparer">The IComparer implementation to use when comparing elements or null
		/// to use the default comparer.</param>
		/// <exception cref="System.InvalidOperationException">Comparar is null and the default comparer
		/// cannot find an IComparable implementation for type T.</exception>
		public void Sort(IComparer<T> comparer)
		{
			_list.Sort(comparer);

			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Sorts the elements in a range of elements in <see cref="Opf3.ObjectSet{T}">ObjectSet</see> 
		/// using the specified comparer.
		/// </summary>
		/// <param name="index">The zero-based starting index of the range to sort.</param>
		/// <param name="count">The length of the range to sort.</param>
		/// <param name="comparer">The IComparer implementation to use when comparing elements or null
		/// to use the default comparer.</param>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or count is less
		/// than zero.</exception>
		/// <exception cref="System.ArgumentException">Index and count do not specify a valid range in the
		/// <see cref="ObjectSet{T}">ObjectSet</see>.</exception>
		/// <exception cref="System.InvalidOperationException">Comparar is null and the default comparer
		/// cannot find an IComparable implementation for type T.</exception>
		public void Sort(int index, int count, IComparer<T> comparer)
		{
			_list.Sort(index, count, comparer);

			// Invoked the event to notify the user that the list has been changed.
			OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
		}

		/// <summary>
		/// Copies the elements of the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> to a new array.
		/// </summary>
		/// <returns>An array containing copies of the elements of the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</returns>
		public T[] ToArray()
		{
			return _list.ToArray();
		}

		/// <summary>
		/// Sets the capacity to the actual number of elements in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		public void TrimExcess()
		{
			_list.TrimExcess();
		}

		/// <summary>
		/// Determines whether every element in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> 
		/// matches the conditions defined by the specified predicate. 
		/// </summary>
		/// <param name="match">The Predicate delegate that defines the conditions to check against the elements.</param>
		/// <returns>true, if every element in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> matches 
		/// the conditions defined by the specified predicate; otherwise, false.</returns>
		/// <exception cref="System.ArgumentNullException">Match is null.</exception>
		public bool TrueForAll(Predicate<T> match)
		{
			return _list.TrueForAll(match);
		}

		#endregion

		/// <summary>
		/// Returns the list of removed objects. Objects that are marked for deletion 
		/// (using the methods of the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>) are moved 
		/// to this list and deleted from storage when the changes are persisted.
		/// </summary>
		public virtual List<T> RemovedList
		{
			get { return _removedList; }
			private set { _removedList = value; }
		}

		/// <summary>
		/// Returns the list that is used to store the persistent objects.
		/// </summary>
		/// <value>The list that contains the persistent objects of the 
		/// <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</value>
		protected List<T> InnerList
		{
			get { return _list; }
		}

		/// <summary>
		/// Returns the default <see cref="ObjectView{T}">ObjectView</see> for the <see cref="ObjectSet{T}">ObjectSet</see>.
		/// This instance may be bound to data-driven controls. When the <see cref="ObjectSet{T}">ObjectSet</see> is bound
		/// to a control this instance is used.
		/// </summary>
		[XmlIgnore]
		public ObjectView<T> DefaultView
		{
			get
			{
				if (_defaultView == null)
					_defaultView = new ObjectView<T>(this);

				return _defaultView;
			}
		}

		/// <summary>
		/// Returns the <see cref="Opf3.ObjectViewManager{T}">ObjectViewManager</see> associated with the <see cref="ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		internal ObjectViewManager<T> ObjectViewManager
		{
			get
			{
				if (_objectViewManager == null)
					_objectViewManager = new ObjectViewManager<T>(this);

				return _objectViewManager;
			}
		}

		#region ICustomPersister<T> Members

		/// <summary>
		/// This method is called during load from the storage by the <see cref="Opf3.ObjectContext">ObjectContext</see> 
		/// class.
		/// </summary>
		/// <param name="context"><see cref="Opf3.ObjectContext">ObjectContext</see> that called this routine.</param>
		/// <param name="query"><see cref="Opf3.Query.IQuery">IQuery</see> that is executed by the context to 
		/// load the objects.</param>
		void ICustomPersister.Load(ObjectContext context, IQuery query)
		{
			Load(context, query, -1);
		}

		/// <summary>
		/// This method is called during load from the storage by the <see cref="Opf3.ObjectContext">ObjectContext</see> 
		/// class.
		/// </summary>
		/// <param name="context"><see cref="Opf3.ObjectContext">ObjectContext</see> that called this routine.</param>
		/// <param name="query"><see cref="Opf3.Query.IQuery">IQuery</see> that is executed by the context to 
		/// load the objects.</param>
		/// <param name="amount">Specifies the maximum amount of objects that have to be loaded from the storage.</param>
		void ICustomPersister.Load(ObjectContext context, IQuery query, int amount)
		{
			Load(context, query, amount);
		}


		/// <summary>
		/// This routine is called by the <see cref="Opf3.ObjectContext">ObjectContext</see>
		/// during save operations on the storage.
		/// <b>Attention:</b> The <see cref="ICustomPersister">ICustomPersister</see> should only add an object 
		/// to the <see cref="PersistingTrace">PersistingTrace</see> (set as argument) if not
		/// using the <see cref="ObjectContext">ObjectContext</see> to save it's persistent object.
		/// </summary>
		/// <param name="context"><see cref="Opf3.ObjectContext">ObjectContext</see> that called this routine.</param>
		/// <param name="persistDepth">Specifies the depth of the object graph that is persisted.</param>
		/// <param name="persistingTrace">Contains information about the recently persistet objects. The 
		/// <see cref="ICustomPersister">ICustomPersister</see> should only add an object to this trace if not
		/// using the <see cref="ObjectContext">ObjectContext</see> to save it's persistent object.</param>
		void ICustomPersister.Persist(ObjectContext context, PersistDepths persistDepth, PersistingTrace persistingTrace)
		{
			Persist(context, persistDepth, persistingTrace);
		}

		/// <summary>
		/// This routine is called by the <see cref="Opf3.ObjectContext">ObjectContext</see>
		/// during save operations on the storage.
		/// <b>Attention:</b> The <see cref="ICustomPersister">ICustomPersister</see> should only add an object 
		/// to the <see cref="PersistingTrace">PersistingTrace</see> (set as argument) if not
		/// using the <see cref="ObjectContext">ObjectContext</see> to save it's persistent object.
		/// </summary>
		/// <param name="context"><see cref="Opf3.ObjectContext">ObjectContext</see> that called this routine.</param>
		/// <param name="persistDepth">Specifies the depth of the object graph that is persisted.</param>
		/// <param name="persistingTrace">Contains information about the recently persistet objects. The 
		/// <see cref="ICustomPersister">ICustomPersister</see> should only add an object to this trace if not
		/// using the <see cref="ObjectContext">ObjectContext</see> to save it's persistent object.</param>
		protected virtual void Persist(ObjectContext context, PersistDepths persistDepth, PersistingTrace persistingTrace)
		{
			// Delete all objects in the RemovedList.
			foreach (T obj in RemovedList)
			{
				if (obj != null)
				{
					context.MarkForDeletion(obj);
					context.PersistChanges(obj, persistDepth);
				}
			}

			ITransactionStorage storage = context.Storage as ITransactionStorage;
			// If the storage is a transaction storage and there is an active transaction
			// we have to move the data from the removed list to a temporary list and register the
			// required events.
			if (storage != null && storage.IsTransactionActive)
			{
				_transactionRemovedList = RemovedList;
				// Create a new list for the RemovedList.
				_removedList = new List<T>();
				storage.TransactionCommit += new EventHandler<EventArgs>(storage_TransactionCommit);
				storage.TransactionRollback += new EventHandler<EventArgs>(storage_TransactionRollback);
			}

			RemovedList.Clear();

			// Save all other objects.
			foreach (T obj in this)
			{
				if (obj != null)
				{
					context.PersistChanges(obj, persistDepth);
				}
			}
		}

		/// <summary>
		/// This method is called during load from the storage by the <see cref="Opf3.ObjectContext">ObjectContext</see> 
		/// class.
		/// </summary>
		/// <param name="context"><see cref="Opf3.ObjectContext">ObjectContext</see> that called this routine.</param>
		/// <param name="query"><see cref="Opf3.Query.IQuery">IQuery</see> that is executed by the context to 
		/// load the objects.</param>
		/// <param name="amount">Specifies the maximum amount of objects that have to be loaded from the storage.
		/// If the value is -1 all objects should be loaded from the storage that are returned by the query.</param>
		protected virtual void Load(ObjectContext context, IQuery query, int amount)
		{
			using (ObjectReader<T> objectReader = context.GetObjectReader<T>(query))
			{
				// Register the event of the ObjectReader.
				objectReader.FetchingStorageRecord += new PersistentTypeSelector(objectReader_FetchingStorageRecord);

				int current = 0;
				while (objectReader.MoveNext() && (amount == -1 || current < amount))
				{
					this.Add(objectReader.Current);
					current++;
				}
			}
		}
		
		/// <summary>
		/// Method is invoked if the transaction on the storage is committed.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void storage_TransactionCommit(object sender, EventArgs e)
		{
			// On a commit we can put away the data.
			_transactionRemovedList = null;

			// The storage here must always be a ITransaction storage. If not we want
			// an exception here.
			ITransactionStorage storage = (ITransactionStorage)sender;
			storage.TransactionCommit -= new EventHandler<EventArgs>(storage_TransactionCommit);
			storage.TransactionRollback -= new EventHandler<EventArgs>(storage_TransactionRollback);
		}

		/// <summary>
		/// Method is invoked if the transaction on the storage is rolled back.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void storage_TransactionRollback(object sender, EventArgs e)
		{
			// Restore the RemovedList on a rollback.
			if (_transactionRemovedList != null)
				_removedList = _transactionRemovedList;
			_transactionRemovedList = null;

			// The storage here must always be a ITransaction storage. If not we want
			// an exception here.
			ITransactionStorage storage = (ITransactionStorage)sender;
			storage.TransactionCommit -= new EventHandler<EventArgs>(storage_TransactionCommit);
			storage.TransactionRollback -= new EventHandler<EventArgs>(storage_TransactionRollback);
		}

		#endregion

		#region Overriden Members

		/// <summary>
		/// Returns the string representation for the object.
		/// </summary>
		public override string ToString()
		{
			return string.Format("{0}, Count = {1}, RemovedList.Count = {2}", base.ToString(), this.Count, this.RemovedList.Count);
		}

		#endregion

		#region Operations on Sets

		/// <summary>
		/// All duplicates in the <see cref="ObjectSet{T}">ObjectSet</see> are removed. Duplicates are identified
		/// by analyzing the identifiers.
		/// </summary>
		public void Distinct()
		{
			SetOperations setOps = new SetOperations();
			setOps.Distinct(this);
		}

		/// <summary>
		/// Unites this instance with another instance of <see cref="ObjectSet{T}">ObjectSet</see>. 
		/// Duplicates are removed (they are identified by analyzing the identifiers of the persistent objects) 
		/// and the current instance of the <see cref="ObjectSet{T}">ObjectSet</see> 
		/// is sorted by identifiers. To have the instance resorted use the 
		/// <see cref="ObjectSet{T}.Sort(Comparison{T})">Sort</see> routine of the <see cref="ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="objectSet">The <see cref="ObjectSet{T}">ObjectSet</see> united with the current instance.</param>
		public void Union(ObjectSet<T> objectSet)
		{
			SetOperations setOps = new SetOperations();
			setOps.Union(this, objectSet);
		}

		/// <summary>
		/// Creates the intersecting set of this instance and another instance of <see cref="ObjectSet{T}">ObjectSet</see>. 
		/// Duplicates are removed (they are identified by analyzing the identifiers of the persistent objects) 
		/// and the current instance of the <see cref="ObjectSet{T}">ObjectSet</see> is sorted by identifiers. To have the 
		/// instance resorted use the <see cref="ObjectSet{T}.Sort(Comparison{T})">Sort</see> routine of the 
		/// <see cref="ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="objectSet">The <see cref="ObjectSet{T}">ObjectSet</see> used to create the intersecting set.</param>
		public void Intersect(ObjectSet<T> objectSet)
		{
			SetOperations setOps = new SetOperations();
			setOps.Intersect(this, objectSet);
		}

		/// <summary>
		/// Creates the difference in quantities of this instance and another instance of 
		/// <see cref="ObjectSet{T}">ObjectSet</see>. All objects being also in the second 
		/// <see cref="ObjectSet{T}">ObjectSet</see> are removed.
		/// Duplicates are removed (they are identified by analyzing the identifiers of the persistent objects) 
		/// and the current instance of the <see cref="ObjectSet{T}">ObjectSet</see> is sorted by identifiers. To have the 
		/// instance resorted use the <see cref="ObjectSet{T}.Sort(Comparison{T})">Sort</see> routine of the 
		/// <see cref="ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="objectSet">The <see cref="ObjectSet{T}">ObjectSet</see> used to create the difference in quantities.</param>
		public void Except(ObjectSet<T> objectSet)
		{
			SetOperations setOps = new SetOperations();
			setOps.Except(this, objectSet);
		}

		#endregion

		#region IListSource Members

		/// <summary>
		/// Returns true since the <see cref="ObjectSet{T}">ObjectSet</see> is a list of objects.
		/// </summary>
		bool IListSource.ContainsListCollection
		{
			get { return true; }
		}

		/// <summary>
		/// Returns a list that contains the objects in the current instance of the 
		/// <see cref="ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <returns>A list that contains the objects of the current instance.</returns>
		IList IListSource.GetList()
		{
			return (IList)this.DefaultView;
		}

		#endregion

		#region IList Members

		/// <summary>
		/// Adds an item to the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="item">The object to add to the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		int IList.Add(object item)
		{
			lock (this)
			{
				this.Add((T)item);
				return this.Count - 1;
			}
		}

		/// <summary>
		/// Removes all items from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		void IList.Clear()
		{
			this.Clear();
		}

		/// <summary>
		/// Determines whether the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> contains a specific object.
		/// </summary>
		/// <param name="item">The object to located in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <returns>True, if item is found in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>; otherwise, false.</returns>
		bool IList.Contains(object item)
		{
			return this.Contains((T)item);
		}

		/// <summary>
		/// Determines the index of a specific item in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		/// <param name="item">The object to locate in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <returns>The index of item if found in the list; otherwise, -1.</returns>
		int IList.IndexOf(object item)
		{
			return this.IndexOf((T)item);
		}

		/// <summary>
		/// Inserts an item to the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> at the specified index. 
		/// </summary>
		/// <param name="index">The zero-based index at which item should be inserted.</param>
		/// <param name="item">The object to insert into the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or index is greater then
		/// <see cref="ObjectSet{T}.Count">Count</see>.</exception>
		void IList.Insert(int index, object item)
		{
			this.Insert(index, (T)item);
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="ObjectSet{T}">ObjectSet</see> has a fixed size.
		/// </summary>
		/// <returns>True if the <see cref="ObjectSet{T}">ObjectSet</see> has a fixed size; otherwise, false.</returns>
		[XmlIgnore]
		bool IList.IsFixedSize
		{
			get { return false; }
		}

		/// <summary>
		/// Gets a value indicating whether the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> is read-only.
		/// </summary>
		/// <value>True, if the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> is read-only; otherwise, false.</value>
		[XmlIgnore]
		bool IList.IsReadOnly
		{
			get { return this.IsReadOnly; }
		}

		/// <summary>
		/// Removes the first occurrence of a specific object from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>
		/// to the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>.
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="item">The object to remove from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		void IList.Remove(object item)
		{
			this.Remove((T)item);
		}

		/// <summary>
		/// Moves the object at the specified index from the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> to
		/// the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see>. 
		/// All objects on the <see cref="Opf3.ObjectSet{T}.RemovedList">RemovedList</see> are deleted 
		/// when the changes on the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> are persisted.
		/// </summary>
		/// <param name="index">The zero-based index of the item to move.</param>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less than zero or index is equal to
		/// or greater than <see cref="ObjectSet{T}.Count">Count</see>.</exception>
		void IList.RemoveAt(int index)
		{
			this.RemoveAt(index);
		}

		/// <summary>
		/// Gets or sets the object at the specified index.
		/// </summary>
		/// <param name="index">The zero-based index of the object to get or set.</param>
		/// <returns>The object at the specified index.</returns>
		/// <exception cref="System.ArgumentOutOfRangeException">Index is less then zero or index is equal or greater
		/// then <see cref="ObjectSet{T}.Count">Count</see>.</exception>
		object IList.this[int index]
		{
			get
			{
				return this[index];
			}
			set
			{
				this[index] = (T)value;
			}
		}

		#endregion

		#region ICollection Members

		/// <summary>
		/// Copies the elements of the <see cref="Opf3.ObjectSet{T}">ObjectSet</see> to an Array, starting at 
		/// a particular Array index.
		/// </summary>
		/// <param name="array">The one-dimensional Array that is the destination of the elements copied 
		/// from <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</param>
		/// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
		/// <exception cref="System.ArgumentNullException">Array is null.</exception>
		/// <exception cref="System.ArgumentException">Array is multidimensional, array does not have zero-based
		/// indexing, the number of elements in the source is greater than the number of elements that the 
		/// destination array can contain or type T cannot be cast automatically to the type of the destination
		/// array.</exception>
		void ICollection.CopyTo(Array array, int arrayIndex)
		{
			this.CopyTo((T[])array, arrayIndex);
		}

		/// <summary>
		/// Gets the number of elements contained in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>. 
		/// </summary>
		/// <value>The number of elements contained in the <see cref="Opf3.ObjectSet{T}">ObjectSet</see>.</value>
		[XmlIgnore]
		int ICollection.Count
		{
			get { return this.Count; }
		}

		/// <summary>
		/// Gets a value indicating whether access to the <see cref="ObjectSet{T}">ObjectSet</see> is synchronized (thread safe).
		/// </summary>
		[XmlIgnore]
		bool ICollection.IsSynchronized
		{
			get { return false; }
		}

		/// <summary>
		/// Gets an object that can be used to synchronize access to the <see cref="ObjectSet{T}">ObjectSet</see>.
		/// </summary>
		[XmlIgnore]
		object ICollection.SyncRoot
		{
			get { return this; }
		}

		#endregion

		#region IRaiseItemChangedEvents Members

		/// <summary>
		/// Gets a value indicating whether the object raises the <see cref="ListChanged">ListChanged</see> event on changes in the managed items.
		/// </summary>
		bool IRaiseItemChangedEvents.RaisesItemChangedEvents
		{
			get { return _raiseItemChangedEvents; }
		}

		#endregion
	}
}